import { useState, useEffect, useRef } from 'react'
import { debounce } from 'lodash-es'
import { getListByPage } from '../mock/index'
import './style/lazyList.css'
import { IList } from '../mock/index'

// 距离阈值，可以看着定义，主要用于判断滚动到底部的条件
const threshold = 1
// 单个 item 高度
const itemHeight = 40
// 获取数据 pageSize
const pageSize = 20

function VirtualListLimitHeight() {
	// list container ref 引用
	const listRef = useRef<HTMLDivElement | null>(null)
	// 分页获取数据的页码引用
	const pageNumRef = useRef<number>(1)
	// 列表数据
	const [list, setList] = useState<IList[]>([])
	// 列表显示区域开始索引
	const [beginIndex, setBeginIndex] = useState<number>(0)
	// 列表显示区域结束索引
	const [endIndex, setEndIndex] = useState<number>(0)

	useEffect(() => {
		// 初始化 list
		const list = getListByPage(pageNumRef.current, pageSize)
		// 获取 list container offsetHeight
		const offsetHeight = listRef.current?.offsetHeight
		// 计算显示区域结束索引，默认展示 2 屏数据
		const endIndex = offsetHeight
			? Math.ceil((offsetHeight * 2) / itemHeight)
			: 0
		setEndIndex(endIndex)
		setList(list)

		// 监听滚动事件
		listRef.current?.addEventListener('scroll', handleScroll)
		// 组件销毁，取消事件监听
		return () => {
			listRef.current?.removeEventListener('scroll', handleScroll)
		}
	}, [])

	// 滑动到底部
	const onReachBottom = () => {
		pageNumRef.current++
		const curList = getListByPage(pageNumRef.current, pageSize)
		setList((list) => list.concat(curList))
	}

	// 滚动事件
	const handleScroll = debounce((e: Event) => {
		const target = e.target
		const { scrollTop, scrollHeight, offsetHeight } =
			target as EventTarget & {
				scrollTop: number
				scrollHeight: number
				offsetHeight: number
			}

		// 滚动到底部，请求下一页数据
		if (scrollHeight - scrollTop < offsetHeight + threshold) {
			onReachBottom()
		} else {
			// 计算开始索引
			let beginIndex = 0
			if (scrollTop > offsetHeight) {
				beginIndex = Math.floor((scrollTop - offsetHeight) / itemHeight)
			}

			// endIndex 设置 2 屏，减少向下滚动出现白屏的可能性
			const endIndex = Math.ceil(
				(scrollTop + offsetHeight * 2) / itemHeight,
			)
			setBeginIndex(beginIndex)
			setEndIndex(endIndex)
		}
	}, 300)

	const getListRender = () => {
		// 计算顶部块和底部块的高度，撑开滚动条
		const beginHeight = beginIndex * itemHeight
		const endHeight =
			(list.length - endIndex < 0 ? 0 : list.length - endIndex) *
			itemHeight
		const listData = list.slice(beginIndex, endIndex + 1)
		return (
			<>
				<div key="begin" style={{ height: beginHeight }}></div>
				{listData.length
					? listData.map((item) => {
							return (
								<div key={item.id} className="item">
									无限滚动-定高虚拟滚动 - {item.value}
								</div>
							)
					  })
					: null}
				<div key="end" style={{ height: endHeight }}></div>
			</>
		)
	}
	return (
		<div className="lazy-list-container" ref={listRef}>
			{getListRender()}
		</div>
	)
}

export default VirtualListLimitHeight
